/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	ComponentTransform.h
*
*	Description -	Linear transform component.
*
*	Comments	-	All nodes probably will contain this transform component
*
*	Modification History:
*			Name			   Date					Description
*			MrCodeSushi	-	06/19/2012	-	Creation of this file
**************************************************************************************/
#ifndef _PSX_TRANSFORM_COMPONENT_H_
#define _PSX_TRANSFORM_COMPONENT_H_

#include "PulseSTD.h"
#include "IComponent.h"
#include "Vector3.h"
#include "ParameterBinderVector3.h"
#include "ParameterBinderMatrixRef.h"
#include "Matrix4x4.h"
#include "Matrix3x3.h"

namespace Pulse
{
	class ComponentTransform : public IComponent
	{
	public:

		ComponentTransform( Actor *pOwner );

		virtual ~ComponentTransform( void );

		virtual EErrorCode::Type Initialize( XMLElementPtr pElem );

		virtual const EComponent::Type GetType( void ) const { return EComponent::TRANSFORM; }

		// All derived components need to have this
		static const EComponent::Type GetType_Static( void ) { return EComponent::TRANSFORM; }

		virtual const CHAR * GetName( void ) const { return PSX_String("Transform"); }

		virtual void Update( FLOAT dt );

		void InvalidateWorldTransform( void ) { m_bUpdateWorld = TRUE; }

		void SetPosition( const Vector3 *pPos );

		void SetPosition( FLOAT x, FLOAT y, FLOAT z );

		// Euler angles x - pitch, y - yaw, z - roll
		void SetRotation( FLOAT x, FLOAT y, FLOAT z );

		// Euler angles x - pitch, y - yaw, z - roll
		void SetRotation( const Vector3 *pRot );

		void SetRotationX( FLOAT angle );

		void SetRotationY( FLOAT angle );

		void SetRotationZ( FLOAT angle );

		Vector3 * GetRotation( void );
 
		void SetScale( const FLOAT scale );

		void SetScale( const Vector3 *pScale );

		void SetTransformLocal( const Matrix4x4 *pTransform );

		void SetTransformWorld( const Matrix4x4 *pTransform );
		
		Vector3 * GetScale( void );

		Matrix4x4 * GetTransformWorld( void );
		const Matrix4x4 * GetTransformWorld( void ) const;

		Matrix4x4 * GetTransformLocal( void );
		const Matrix4x4 * GetTransformLocal( void ) const;

	private:

		// Delegates
		// This won't be used. Components manuall set the local/world
		//	then this component sends an event to delegate to all other components
		void OnNewTransform( IEvent *pEvent );

	private:

		// Local transformations for easy manipulation
		Vector3 m_position;
		Vector3 m_rotation;
		Vector3 m_scale;

		// Final local and world matrices
		BOOL		m_bUpdateLocal;
		BOOL		m_bUpdateWorld;
		BOOL		m_bSendTransformEvent;
		Matrix4x4	m_local;
		Matrix4x4	m_world;

		// Cache transform event for speed
		EventPtr m_pEventTransform;

	};

	PSX_INLINE void ComponentTransform::SetPosition( const Vector3 *pPos )
	{
		m_position = *pPos;
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE void ComponentTransform::SetPosition( FLOAT x, FLOAT y, FLOAT z )
	{
		m_position.Set( x, y, z );
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE void ComponentTransform::SetRotation( FLOAT x, FLOAT y, FLOAT z )
	{
		m_rotation.Set( x, y, z );
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE void ComponentTransform::SetRotation( const Vector3 *pRot ) 
	{
		m_rotation = *pRot; 
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE void ComponentTransform::SetRotationX( FLOAT angle ) 
	{
		m_rotation.x = angle;
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE void ComponentTransform::SetRotationY( FLOAT angle ) 
	{ 
		m_rotation.y = angle; 
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE void ComponentTransform::SetRotationZ( FLOAT angle )
	{ 
		m_rotation.z = angle;
		m_bUpdateLocal = TRUE;
	}

	PSX_INLINE Vector3 * ComponentTransform::GetRotation( void ) 
	{ 
		return &m_rotation; 
	}

	PSX_INLINE void ComponentTransform::SetScale( const FLOAT scale )
	{
		m_scale.Set( scale, scale, scale );
		m_bUpdateLocal = TRUE;
	}
 
	PSX_INLINE void ComponentTransform::SetScale( const Vector3 *pScale ) 
	{ 
		m_scale = *pScale;
		m_bUpdateLocal = TRUE;
	}
		
	PSX_INLINE Vector3 * ComponentTransform::GetScale( void ) 
	{ 
		return &m_scale;
	}

	PSX_INLINE void ComponentTransform::SetTransformLocal( const Matrix4x4 *pTransform )
	{
		m_local = *pTransform;
		InvalidateWorldTransform();
	}

	PSX_INLINE Matrix4x4 * ComponentTransform::GetTransformWorld( void ) 
	{ 
		return &m_world;
	}

	PSX_INLINE const Matrix4x4 * ComponentTransform::GetTransformWorld( void ) const
	{ 
		return &m_world;
	}

	PSX_INLINE Matrix4x4 * ComponentTransform::GetTransformLocal( void ) 
	{
		return &m_local;
	}

	PSX_INLINE const Matrix4x4 * ComponentTransform::GetTransformLocal( void ) const
	{
		return &m_local;
	}
}

#endif /* _PSX_TRANSFORM_COMPONENT_H_ */